using System.Data.Common;
using System.Net;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.Data.SqlClient;
using Oracle.ManagedDataAccess.Client;
using Lzfy_His_Service.Services;

namespace Lzfy_His_Service.Middleware
{
    /// <summary>
    /// 全局异常处理中间件
    /// </summary>
    public class GlobalExceptionHandlingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<GlobalExceptionHandlingMiddleware> _logger;
        private readonly IHostApplicationLifetime _appLifetime;
        private readonly IWebHostEnvironment _env;
        private readonly IServiceProvider _serviceProvider;
        
        // 数据库错误计数器
        private static int _consecutiveDbErrors = 0;
        private static DateTime _lastDbErrorResetTime = DateTime.Now;
        private static readonly object _errorCountLock = new object();
        private static readonly TimeSpan _dbErrorResetInterval = TimeSpan.FromMinutes(5);
        private const int _maxConsecutiveDbErrors = 20;
        
        // 异常统计
        private static readonly Dictionary<string, int> _exceptionCounts = new Dictionary<string, int>();
        private static DateTime _lastStatsResetTime = DateTime.Now;
        private static readonly TimeSpan _statsResetInterval = TimeSpan.FromHours(1);
        private const int _criticalExceptionThreshold = 10;

        public GlobalExceptionHandlingMiddleware(
            RequestDelegate next,
            ILogger<GlobalExceptionHandlingMiddleware> logger,
            IHostApplicationLifetime appLifetime,
            IWebHostEnvironment env,
            IServiceProvider serviceProvider)
        {
            _next = next;
            _logger = logger;
            _appLifetime = appLifetime;
            _env = env;
            _serviceProvider = serviceProvider;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                // 记录异常统计
                RecordExceptionStatistics(ex);
                
                _logger.LogError(ex, "全局异常处理: {ExceptionType} - {Message} - RequestPath: {RequestPath}", 
                    ex.GetType().Name, ex.Message, context.Request.Path);
                    
                await HandleExceptionAsync(context, ex);
                
                // 检查是否是数据库异常
                if (IsDbException(ex))
                {
                    HandleDatabaseException(ex);
                }
            }
        }

        private async Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            context.Response.ContentType = "application/json";
            
            var (statusCode, errorMessage, errorType) = ClassifyException(exception);
            
            var response = new
            {
                error = new
                {
                    type = errorType,
                    message = errorMessage,
                    details = _env.IsDevelopment() ? exception.Message : "请联系系统管理员",
                    timestamp = DateTime.Now,
                    traceId = System.Diagnostics.Activity.Current?.Id ?? context.TraceIdentifier,
                    requestPath = context.Request.Path.Value
                }
            };

            // 检查是否为数据库相关异常
            if (IsDbException(exception))
            {
                HandleDatabaseException(exception);
                statusCode = 503; // Service Unavailable
            }
            else
            {
                // 重置数据库错误计数
                ResetDatabaseErrorCountIfNeeded();
            }

            context.Response.StatusCode = statusCode;

            var jsonResponse = JsonSerializer.Serialize(response, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            });

            await context.Response.WriteAsync(jsonResponse);
        }

        /// <summary>
        /// 分类异常并返回相应的状态码和错误信息
        /// </summary>
        private (int statusCode, string errorMessage, string errorType) ClassifyException(Exception exception)
        {
            return exception switch
            {
                ArgumentException => (400, "请求参数错误", "ValidationError"),
                UnauthorizedAccessException => (401, "未授权访问", "AuthenticationError"),
                TimeoutException => (408, "请求超时", "TimeoutError"),
                NotImplementedException => (501, "功能未实现", "NotImplementedError"),
                BadHttpRequestException => (400, "请求格式错误", "BadRequestError"),
                _ when IsDbException(exception) => (503, "数据库服务暂时不可用", "DatabaseError"),
                _ => (500, "服务器内部错误", "InternalServerError")
            };
        }

        /// <summary>
        /// 重置数据库错误计数（如果需要）
        /// </summary>
        private void ResetDatabaseErrorCountIfNeeded()
        {
            lock (_errorCountLock)
            {
                if (DateTime.Now - _lastDbErrorResetTime > _dbErrorResetInterval)
                {
                    _consecutiveDbErrors = 0;
                }
            }
        }

        /// <summary>
        /// 判断是否为数据库异常
        /// </summary>
        private bool IsDbException(Exception ex)
        {
            return ex is DbException || // 通用数据库异常
                   ex is SqlException || // SQL Server异常
                   ex is OracleException || // Oracle异常
                   ex is TimeoutException || // 超时异常
                   (ex.InnerException != null && IsDbException(ex.InnerException));
        }

        /// <summary>
        /// 判断是否为关键SQL异常
        /// </summary>
        private bool IsCriticalSqlException(SqlException sqlException)
        {
            // SQL Server关键错误代码
            var criticalErrorNumbers = new[] { 2, 53, 18456, 1205, 8645, 8651 };
            return criticalErrorNumbers.Contains(sqlException.Number);
        }

        /// <summary>
        /// 判断是否为关键Oracle异常
        /// </summary>
        private bool IsCriticalOracleException(OracleException oracleException)
        {
            // Oracle关键错误代码
            var criticalErrorNumbers = new[] { 1017, 1033, 1034, 3113, 3114, 12154, 12505, 12514, 12541 };
            return criticalErrorNumbers.Contains(oracleException.Number);
        }

        /// <summary>
        /// 记录异常统计
        /// </summary>
        private void RecordExceptionStatistics(Exception exception)
        {
            lock (_errorCountLock)
            {
                var exceptionType = exception.GetType().Name;
                
                // 重置统计（如果需要）
                if (DateTime.Now - _lastStatsResetTime > _statsResetInterval)
                {
                    _exceptionCounts.Clear();
                    _lastStatsResetTime = DateTime.Now;
                }
                
                // 记录异常计数
                if (_exceptionCounts.ContainsKey(exceptionType))
                {
                    _exceptionCounts[exceptionType]++;
                }
                else
                {
                    _exceptionCounts[exceptionType] = 1;
                }
                
                // 检查是否达到关键阈值
                if (_exceptionCounts[exceptionType] >= _criticalExceptionThreshold)
                {
                    _logger.LogWarning("异常类型 {ExceptionType} 在过去1小时内发生了 {Count} 次", 
                        exceptionType, _exceptionCounts[exceptionType]);
                }
            }
        }

        /// <summary>
        /// 处理数据库异常
        /// </summary>
        private void HandleDatabaseException(Exception exception)
        {
            lock (_errorCountLock)
            {
                var now = DateTime.Now;
                
                // 如果距离上次数据库错误超过重置间隔，重置计数器
                if (now - _lastDbErrorResetTime > _dbErrorResetInterval)
                {
                    _consecutiveDbErrors = 0;
                }
                
                _lastDbErrorResetTime = now;
                _consecutiveDbErrors++;
                
                // 记录详细的数据库错误信息
                var dbErrorDetails = GetDatabaseErrorDetails(exception);
                var errorType = dbErrorDetails.ContainsKey("ErrorCategory") ? dbErrorDetails["ErrorCategory"] : "未知错误";
                var details = JsonSerializer.Serialize(dbErrorDetails, new JsonSerializerOptions { WriteIndented = true });
                
                _logger.LogError(
                    "数据库异常详情 (连续错误: {Count}/{Threshold}):\n错误类型: {ErrorType}\n错误消息: {Message}\n堆栈跟踪: {StackTrace}\n错误详情: {Details}",
                    _consecutiveDbErrors, _maxConsecutiveDbErrors, 
                    errorType, exception.Message, 
                    exception.StackTrace, details);
                
                // 如果连续错误达到阈值，记录严重警告但不停止应用
                    if (_consecutiveDbErrors >= _maxConsecutiveDbErrors)
                    {
                        _logger.LogCritical(
                            "数据库连续错误达到阈值 ({Count}次)，系统需要进一步诊断",
                            _consecutiveDbErrors);
                        
                        // 重置计数器，避免重复触发
                        _consecutiveDbErrors = 0;
                    }
            }
        }

        /// <summary>
        /// 获取数据库错误详情（增强版，返回结构化数据）
        /// </summary>
        private Dictionary<string, object> GetDatabaseErrorDetails(Exception exception)
        {
            var details = new Dictionary<string, object>();
            var baseException = exception.GetBaseException();

            // 添加基础异常信息
            details.Add("BaseExceptionType", baseException.GetType().Name);
            details.Add("BaseExceptionMessage", baseException.Message);
            details.Add("FullExceptionType", exception.GetType().FullName ?? "Unknown");
            details.Add("ExceptionHash", exception.GetHashCode());
            details.Add("Timestamp", DateTime.UtcNow);
            details.Add("StackTrace", exception.StackTrace ?? string.Empty);

            switch (exception)
            {
                case SqlException sqlEx:
                    details.Add("DatabaseType", "SQLServer");
                    details.Add("ErrorNumber", sqlEx.Number);
                    details.Add("ErrorCode", sqlEx.ErrorCode);
                    details.Add("Server", sqlEx.Server);
                    details.Add("Procedure", sqlEx.Procedure);
                    details.Add("LineNumber", sqlEx.LineNumber);
                    details.Add("Class", sqlEx.Class);
                    details.Add("State", sqlEx.State);
                    details.Add("ErrorsCount", sqlEx.Errors.Count);
                    
                    // 添加所有SQL错误的详细信息
                    var sqlErrors = new List<Dictionary<string, object>>();
                    foreach (SqlError error in sqlEx.Errors)
                    {
                        sqlErrors.Add(new Dictionary<string, object>
                        {
                            {"Number", error.Number},
                            {"Message", error.Message},
                            {"Procedure", error.Procedure ?? string.Empty},
                            {"LineNumber", error.LineNumber},
                            {"Server", error.Server},
                            {"Source", error.Source},
                            {"State", error.State}
                        });
                    }
                    details.Add("SqlErrors", sqlErrors);
                    
                    // 添加错误分类
                    details.Add("ErrorCategory", GetSqlErrorCategory(sqlEx.Number));
                    break;

                case OracleException oracleEx:
                    details.Add("DatabaseType", "Oracle");
                    details.Add("ErrorNumber", oracleEx.Number);
                    details.Add("DataSource", oracleEx.DataSource);
                    details.Add("Procedure", oracleEx.Procedure);
                    details.Add("ErrorsCount", oracleEx.Errors.Count);
                    
                    // 添加所有Oracle错误的详细信息
                    var oracleErrors = new List<Dictionary<string, object>>();
                    for (int i = 0; i < oracleEx.Errors.Count; i++)
                    {
                        var error = oracleEx.Errors[i];
                        oracleErrors.Add(new Dictionary<string, object>
                        {
                            {"Number", error.Number},
                            {"Message", error.Message},
                            {"DataSource", error.DataSource},
                            {"Procedure", error.Procedure ?? string.Empty}
                        });
                    }
                    details.Add("OracleErrors", oracleErrors);
                    
                    // 添加Oracle错误分类
                    details.Add("ErrorCategory", GetOracleErrorCategory(oracleEx.Number));
                    break;

                case TimeoutException timeoutEx:
                    details.Add("DatabaseType", "Unknown");
                    details.Add("ErrorType", "Timeout");
                    details.Add("TimeoutDuration", "配置值或默认30秒");
                    details.Add("Message", timeoutEx.Message);
                    break;

                case DbException dbEx:
                    details.Add("DatabaseType", "Generic");
                    details.Add("ErrorCode", dbEx.ErrorCode);
                    details.Add("Message", dbEx.Message);
                    details.Add("Source", dbEx.Source ?? "Unknown");
                    break;

                default:
                    details.Add("DatabaseType", "Unknown");
                    details.Add("Message", exception.Message);
                    details.Add("Source", exception.Source ?? "Unknown");
                    break;
            }

            return details;
        }

        /// <summary>
        /// 获取SQL Server错误分类
        /// </summary>
        private string GetSqlErrorCategory(int errorNumber)
        {
            return errorNumber switch
            {
                2 or 53 or 258 or 10060 => "连接错误",
                1204 or 1205 or 1222 => "锁相关错误",
                2627 or 2601 => "唯一约束冲突",
                515 => "空值约束冲突",
                547 => "外键约束冲突",
                8152 => "字符串截断",
                8115 => "算术溢出",
                8156 => "参数过多",
                208 => "对象不存在",
                2812 => "存储过程不存在",
                18456 => "登录失败",
                4060 => "数据库不存在",
                11001 => "主机未找到",
                -2 => "超时",
                _ => $"其他错误({errorNumber})"
            };
        }

        /// <summary>
        /// 获取Oracle错误分类
        /// </summary>
        private string GetOracleErrorCategory(int errorNumber)
        {
            return errorNumber switch
            {
                1 => "唯一约束冲突",
                1400 => "非空约束冲突",
                2291 or 2292 => "外键约束冲突",
                1438 => "数值精度超出",
                12899 => "字符串长度超出",
                942 => "表或视图不存在",
                904 => "列不存在",
                1031 => "权限不足",
                1017 => "用户名/密码错误",
                12154 or 12170 or 12514 or 12535 or 12560 => "连接错误",
                28000 => "账户锁定",
                28001 => "密码过期",
                12541 => "无法解析服务名",
                12543 => "目标主机不可达",
                _ => $"其他错误({errorNumber})"
            };
        }
        
        /// <summary>
        /// 触发数据库健康检查（已移除）
        /// </summary>
        private void TriggerDatabaseHealthCheck()
        {
            _logger.LogWarning("数据库健康检查服务已移除，跳过健康检查");
        }
    }

    // 扩展方法，用于在Startup中注册中间件
    public static class GlobalExceptionHandlingMiddlewareExtensions
    {
        public static IApplicationBuilder UseGlobalExceptionHandling(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<GlobalExceptionHandlingMiddleware>();
        }
    }
}